package com.jarveis.frame.jdbc;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;

import com.jarveis.frame.bean.BeanProperty;
import com.jarveis.frame.cache.CacheManager;
import com.jarveis.frame.io.Resource;
import com.jarveis.frame.lang.CharacterUtil;
import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * Jdbc配置类
 * 
 * @author liuguojun
 */
public class JdbcManager {

	private static Logger logger = Logger.getLogger(JdbcManager.class);
	private static HashMap<String, DataSourceWrapper> dataSources = new HashMap<String, DataSourceWrapper>();
	private static DataSourceWrapper dataSourceWrapper;
	private static final ThreadLocal<DataSourceWrapper> dsThreadLocal = new ThreadLocal<DataSourceWrapper>();
	private static final ThreadLocal<Connection> cnThreadLocal = new ThreadLocal<Connection>();

	public static final String CONFIG_JDBC = "config.xml";
	public static final String CACHE_JDBC_SQL = "cache_jdbc_sql";
	public static final String CACHE_CORE_TABLE = "cache_core_table";

	/**
	 * 初始化配置
	 */
	public static void init() {
		init(CONFIG_JDBC);
	}

	/**
	 * 初始化配置
	 * 
	 * @param filePath
	 */
	public static void init(String filePath) {
		try {
			parseConfig(filePath);
		} catch (Exception ex) {
			logger.error("加载数据源出错！", ex);
		}
	}

	/**
	 * 设置配置文件
	 * 
	 * @param fileName
	 * @return Properties
	 * @throws Exception
	 */
	private static void parseConfig(String filePath) throws Exception {
		CacheManager.getInstance().putCache(CACHE_JDBC_SQL, CacheManager.Mode.Fixed);
		CacheManager.getInstance().putCache(CACHE_CORE_TABLE, CacheManager.Mode.Fixed);

		Document document = Jsoup.parse(Resource.getStream(filePath),
				CharacterUtil.UTF8, "", Parser.xmlParser());
		Elements dss = document.select("jdbcConfig > datasource");
		for (Element ds : dss) {
			parseDataSource(ds);
		}

		Elements sfs = document.select("jdbcConfig > sql-files");
		for (Element sf : sfs) {
			Elements fs = sf.select("file");
			for (Element f : fs) {
				String path = f.attr("path");
				parseSQL(path);
			}
		}
	}

	/**
	 * 解析数据源
	 * 
	 * @param element
	 * @throws Exception
	 */
	private static void parseDataSource(Element element) throws Exception {
		Properties prop = new Properties();

		String id = element.attr("id");
		String df = element.attr("default");
		Elements pps = element.select("property");
		for (Element pp : pps) {
			String name = pp.attr("name");
			String value = pp.attr("value");
			prop.put(name, value);
		}

		ComboPooledDataSource c3p0 = new ComboPooledDataSource();
		BeanProperty.copyProperties(prop, c3p0);

		DataSourceWrapper dsw = new DataSourceWrapper();
		dsw.setId(id);
		dsw.setDf(df == null ? false : Boolean.parseBoolean(df));
		dsw.setDataSource(c3p0);

		if (dsw.getDf()) {
			dataSourceWrapper = dsw;
		}

		dataSources.put(dsw.getId(), dsw);
	}

	/**
	 * 解析SQL配置文件
	 * 
	 * @param path
	 * @throws Exception
	 */
	private static void parseSQL(String filePath) throws Exception {
		Document document = Jsoup.parse(Resource.getStream(filePath),
				CharacterUtil.UTF8, "", Parser.xmlParser());

		Elements ss = document.select("sql");
		for (Element s : ss) {
			String key = s.attr("id").trim();
			if (key.trim().length() == 0) {
				throw new Exception("sql标签的id属性不能包含违法字符(如空格等字符)");
			}
			String value = s.text();
			CacheManager.getInstance().putCacheValue(CACHE_JDBC_SQL, key, value);
		}
	}

	/**
	 * 返回当前线程使用的数据源
	 * 
	 * @return DataSource
	 * @throws Exception
	 */
	public static synchronized DataSource getDataSource() throws Exception {
		DataSourceWrapper dsw = dsThreadLocal.get();
		if (dsw == null) {
			if (dataSourceWrapper != null) {
				dsw = dataSourceWrapper;
				dsThreadLocal.set(dsw);
			} else {
				Exception ex = new Exception("数据源不能被发现, 请检查相关配置或是否初始化配置");
				logger.error(ex.getMessage(), ex);
				throw ex;
			}
		}
		return (DataSource) dsw.getDataSource();
	}

	/**
	 * 改变当前线程使用的数据源
	 * 
	 * @param key
	 * @return DataSource
	 * @throws SQLException
	 */
	public static synchronized void changeDataSource(String key)
			throws SQLException {
		DataSourceWrapper dsw = dataSources.get(key);
		if (dsw == null) {
			throw new SQLException(key + "数据源不能够被发现");
		}
		dsThreadLocal.set(dsw);
	}

	/**
	 * 获取数据库连接
	 * 
	 * @return Connection
	 */
	public static synchronized Connection getConnection() {
		Connection conn = null;

		try {
			conn = cnThreadLocal.get();
			if (conn == null) {
				conn = getDataSource().getConnection();
				cnThreadLocal.set(conn);
			}
		} catch (Exception ex) {
			logger.error("获取数据库连接出错", ex);
		}

		return conn;
	}

	/**
	 * 关闭数据库连接
	 * 
	 * @return
	 */
	public static synchronized void closeConnection() {
		Connection conn = cnThreadLocal.get();

		try {
			if (conn != null && !conn.isClosed()) {
				conn.close();
				cnThreadLocal.remove();
			}
		} catch (Exception ex) {
			logger.error("获取数据库连接出错", ex);
		}
	}

}
